#!/usr/bin/env perl
#
# Copyright 2006-2024 Roy Hills
#
# This file is part of arp-scan.
#
# arp-scan is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# arp-scan is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with arp-scan.  If not, see <http://www.gnu.org/licenses/>.
#
# get-oui -- Fetch the MAC/Vendor registry data from the IEEE website
#
# Author: Roy Hills
# Date: 16 March 2006
#
# This script downloads the Ethernet MAC/Vendor registry data from the
# IEEE website, and converts it to the format used by arp-scan.
#
use warnings;
use strict;
use Getopt::Std;
use LWP::UserAgent;
use Text::CSV;
#
# Use the file:// URLs to use the data from the debian ieee-data package.
# Use the http:// URLs to use the data from the IEEE website.
#
# The entries will be written to the output in alphabetical key order, not
# the order they are listed in the hash.
my %ieee_reg_urls = (
#   OUI   => 'file:///usr/share/ieee-data/oui.csv',
#   MAM   => 'file:///usr/share/ieee-data/mam.csv',
#   OUI36 => 'file:///usr/share/ieee-data/oui36.csv',
#   IAB   => 'file:///usr/share/ieee-data/iab.csv',
   OUI   => 'https://standards-oui.ieee.org/oui/oui.csv',
   MAM   => 'https://standards-oui.ieee.org/oui28/mam.csv',
   OUI36 => 'https://standards-oui.ieee.org/oui36/oui36.csv',
   IAB   => 'https://standards-oui.ieee.org/iab/iab.csv'
);
my $default_filename='ieee-oui.txt';
#
my $usage =
qq/Usage: get-oui [options]
Fetch the Ethernet MAC-Vendor registry data from the IEEE website
and save it in the format used by arp-scan.

'options' is one or more of:
        -h Display this usage message.
        -f FILE Specify the output file. Default=$default_filename
        -v Give verbose progress messages.
/;
my %opts;
my $verbose;
my $filename;
my $url;
my $key;
my $status;
my $line;
my @columns;
my $lineno;
my $total_entries=0;
#
# Process options
#
die "$usage\n" unless getopts('hf:u:v',\%opts);
if ($opts{h}) {
   print "$usage\n";
   exit(0);
}
if (defined $opts{f}) {
   $filename=$opts{f};
} else {
   $filename=$default_filename;
}
$verbose=$opts{v} ? 1 : 0;
#
# If the output filename already exists, rename it to filename.bak before
# we create the new output file.
#
if (-f $filename) {
   print "Renaming $filename to $filename.bak\n" if $verbose;
   rename $filename, "$filename.bak" || die "Could not rename $filename to $filename.bak\n";
}
#
# Open the output file for writing.
#
print "Opening $filename for output\n" if $verbose;
open OUTPUT, '>:encoding(UTF-8)', $filename || die "Could not open $filename for writing";
#
# Write the header comments to the output file.
#
my ($sec,$min,$hour,$mday,$mon,$year,undef,undef,undef) = localtime();
$year += 1900;
$mon++;
my $date_string = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year, $mon, $mday,
                          $hour, $min, $sec);
my $header_comments =
qq/# ieee-oui.txt -- IEEE Ethernet OUI-Vendor mapping file for arp-scan
#
# This file contains the IEEE Ethernet MAC address registry entries that are
# used to determine the Ethernet vendor for a given MAC address.
#
# Each line of this file contains an OUI-vendor mapping in the form:
#
# <MAC-Prefix><TAB><Vendor>
#
# Where <MAC-Prefix> is the prefix of the MAC address in hex, and <Vendor>
# is the name of the vendor.  The prefix can be of any length from two hex
# digits (one octet) to twelve hex digits (six octets, the entire Ethernet
# hardware address).
#
# The order of entries in this file are not important.
#
# arp-scan will attempt to match larger prefixes before trying to match
# smaller ones, and will stop at the first match.
#
# Blank lines and lines beginning with "#" are ignored.
#
# This file was automatically generated by get-oui at $date_string
#
# Do not edit this file.  If you want to add additional MAC-Vendor mappings,
# edit the file mac-vendor.txt instead.
#
/;
print OUTPUT $header_comments;
#
# Initialise Text::CSV object interface
#
my $csv = Text::CSV->new ({ binary => 1, auto_diag => 1 });
#
# For each IEEE registry URL...
#
foreach $key (sort keys %ieee_reg_urls) {
   $url = $ieee_reg_urls{$key};
#
# Fetch the content from the URL
#
   print "Processing IEEE $key registry data from $url\n" if $verbose;
   my $ua = LWP::UserAgent->new;
   my $res = $ua->get($url);
   die $res->status_line unless $res->is_success;
   my $content = $res->content;
   my $content_length = length($content);
   die "Zero-sized response from from $url\n" unless ($content_length > 0);
   print "\tDownloaded $content_length bytes\n" if $verbose;
#
# Parse content and write MAC and Vendor fields to output file.
#
   open(my $fh, '<:encoding(UTF-8)', \$content) || die "Could not open handle to content";
   $csv->header($fh);
   print OUTPUT "\n#\n# Start of IEEE $key registry data\n#\n";
   $lineno=0;
   while (my $row = $csv->getline ($fh)) {
      my $mac = ${$row}[1];
      my $vendor = ${$row}[2];
      $vendor =~ s/^\s+|\s+$//g;	# Remove leading and trailing whitespace
      print OUTPUT "$mac\t$vendor\n";
      $lineno++;
   }
   print OUTPUT "#\n# End of IEEE $key registry data. $lineno entries.\n#\n";
   print "\t$lineno $key entries written to $filename\n" if $verbose;
   $total_entries += $lineno;
}
#
# All done.  Close the output file and print OUI entry count
#
close OUTPUT || die "Error closing output file\n";
print "\nTotal of $total_entries MAC/Vendor mappings written to $filename\n";
